# -*- coding: utf-8 -*-
# created on 2016/5/25

import copy
import itertools
import re
from sympy import Set, latex, FiniteSet, EmptySet, sympify, Symbol, Interval, Union, Eq
from mathsolver import functions
from mathsolver.functions.base import BaseValue, print_ans, search_multiple, PieceWise
from mathsolver.functions.base import getobj
from mathsolver.functions.base import objects as base
from mathsolver.functions.base.exs_objects import BaseErXiangShi, BaseErXiangShiValue
from mathsolver.functions.base.fs_objects import BaseComplexValue
from mathsolver.functions.base.sl_objects import BaseShuLieDengCha, BaseShuLieDengBi, BaseShuLieValue, BaseSequence
from mathsolver.functions.base.xl_objects import BaseVectorValue, BaseVectorSanJiaoXing, BaseVectorQuadra
from mathsolver.functions.sanjiao.sanjiaohanshu import SanJiaoHanShu
from mathsolver.functions.fangcheng import FangChenBuDengShi002, FangChenBuDengShi001
from mathsolver.functions.geometry.geosolver import GeoSolver
from mathsolver.functions.hanshu.hengchengli import HengChengLiCunZaiXing
from mathsolver.functions.hanshu.hengchengli import HengChengLiRenYiXing
from mathsolver.functions.logic import MtHypo
from mathsolver.functions.mathematica.mathematicaSolve import MathematicaSolve, MathematicaOptimize
from mathsolver.functions.process import ProcessExpression
from mathsolver.functions.sanjiao.sanjiao_objects import SanJiaoXing
from mathsolver.functions.sanjiao.triangle_objects import BaseSanJiaoXing
from mathsolver.functions.sympy_utils import interval_2_ineqs


def to_set(x):
    return x if isinstance(x, Set) else FiniteSet(x)


def symbol_value_update(known, symbol_value, is_or):
    known_value = [(k, v) for k, v in known.items() if not isinstance(k, str)]
    if len(known_value) == 0:
        known.update(symbol_value)
    elif len(known_value) == 1:
        known_key, known_value = known_value[0]
        new_key, new_value = list(symbol_value.items())[0]

        if isinstance(known_value, (Interval, Union)):
            known_value_pairs = [{known_key: known_value}]
        else:
            known_value_pairs = [
                dict([(k, to_set(v)) for k, v in zip(known_key, value)]) if isinstance(known_key, tuple) else {
                    known_key: to_set(value)} for value in known_value]

        if isinstance(new_value, (Interval, Union)):
            new_value_pairs = [{new_key: new_value}]
        else:
            new_value_pairs = [
                dict([(k, to_set(v)) for k, v in zip(new_key, value)]) if isinstance(new_key, tuple) else {
                    new_key: to_set(value)} for value in new_value]

        all_values = []
        for p1 in known_value_pairs:
            for p2 in new_value_pairs:
                all_value = dict(p1)
                for k, v in p2.items():
                    if k in all_value:
                        value = all_value[k].intersect(p2[k])
                    else:
                        value = p2[k]

                    if isinstance(value, FiniteSet):
                        value = value.args[0]

                    all_value[k] = value

                all_value = dict([(k, v.args[0]) if isinstance(v, FiniteSet) else (k, v) for k, v in all_value.items()])
                all_values.append(all_value)

        all_key = tuple(all_values[0].keys())
        all_key = all_key if len(all_key) > 1 else all_key[0]
        all_value = [tuple(list(value.values())) if len(value) > 1 else list(value.values())[0] for value in all_values]
        all_value = all_value[0] if len(all_value) == 1 and isinstance(all_value[0], Set) else FiniteSet(*all_value)

        known.pop(known_key)
        known[all_key] = all_value
    else:
        raise ValueError


def known_update(known, item, is_or=False):
    if isinstance(item, GeoSolver):
        known["geosolver"] = item
    elif isinstance(item, base.BaseShanXing):
        known["shanxing"] = item
    elif isinstance(item, BaseSanJiaoXing):
        known["triangle"] = item
    elif isinstance(item, (base.BaseInnerSet, base.BaseMingTiInner, base.BaseHypo)):
        name = item.value["name"]
        known[name] = item
    elif isinstance(item, (base.BaseRootFunc, BaseShuLieDengCha, BaseShuLieDengBi, BaseSequence, BaseErXiangShi,
                           BaseVectorSanJiaoXing, BaseVectorQuadra)):
        name = item.name
        known[name] = item
    elif isinstance(item, base.BaseZhiXians):
        names = []
        for tt in item.value:
            names.append(tt['name'])
        if len(set(names)) == 1:
            known[names[0]] = item
    elif isinstance(item, (BaseVectorValue, BaseComplexValue, BaseShuLieValue, BaseErXiangShiValue)):
        if is_or:
            for k, v in item.value.items():
                if k in known:
                    known[k] += v
                else:
                    known[k] = v
        else:
            known.update(item.value)
    elif isinstance(item, base.BaseSymbolValue):
        if is_or:
            for k, v in item.value.items():
                if k in known:
                    known[k] += v
                else:
                    known[k] = v
        else:
            for k, v in item.value.items():
                if k in known and not v.free_symbols:
                    known[k] = known[k].intersection(v)
                else:
                    known[k] = v

    elif isinstance(item, (base.BasePoint, BaseSequence, base.BaseGraph, BaseSequence, BaseErXiangShi)):
        if isinstance(item, base.BasePoint):
            if item.name:
                known[item.name] = item
        else:
            known[item.name] = item
    elif isinstance(item, SanJiaoHanShu):
        known["sanjiaohansh"] = item
    elif isinstance(item, SanJiaoXing):
        known["sanjiaoxing"] = item
    elif isinstance(item, base.BaseEq):
        symbols = item.free_symbols()
        ineqs = [ineq for ineq in known["inequations"] if set(base.BaseIneq(ineq).free_symbols()).intersection(symbols)]
        ineqs = base.BaseIneqs(ineqs + [item.value])
        if 0 < len(symbols) < 3:
            symbols = [str(sym) for sym in symbols if "_" in str(sym)]
            if not symbols:
                symbols = item.free_symbols()
            try:
                ans = MathematicaSolve().solver(ineqs, base.BaseVariables(symbols)).output[0].value
                key, value = list(ans.items())[0]
                if len(value) == 1 and not isinstance(key, tuple) and key.is_Symbol:
                    known.update(ans)
                    return True
            except Exception:
                pass
        if "equations" in known:
            known["equations"].append(item.value)
        else:
            known["equations"] = [item.value]
    elif isinstance(item, base.BaseEqs):
        symbols = item.free_symbols()
        ineqs = [ineq for ineq in known["inequations"] if set(base.BaseIneq(ineq).free_symbols()).intersection(symbols)]
        ineqs = base.BaseIneqs(ineqs + item.value)
        if len(symbols) - len(item.value) < 2:
            try:
                ans = MathematicaSolve().solver(ineqs, base.BaseVariables(symbols)).output[0].value
                key, value = list(ans.items())[0]
                if len(value) == 1 and "Indeterminate" not in str(value):
                    known.update(ans)
                    return True
            except Exception:
                pass

        known["equations"].extend(item.value)

    elif isinstance(item, base.BaseOptimize):
        known["optimize"].append(item)

    elif isinstance(item, base.BasePieceWiseFunc):
        known["func_piecewise"].append([item.interval, item.var, item.expression])

    elif isinstance(item, base.BaseFuncEq):
        if isinstance(item.value, list):
            value = Eq(*item.sympify())
        else:
            value = item.value
        known["func_equations"].append(value)
    elif isinstance(item, base.BaseFuncAssign):
        known["func_assign"].update(item.assign)
    elif isinstance(item, base.BaseIneqs):
        flag = False
        try:
            symbol_value = FangChenBuDengShi002().solver(item).output[0]
            ans = symbol_value.value
            key, value = list(ans.items())[0]
            assert not isinstance(value, EmptySet)
            if isinstance(value, Set) and key.is_Symbol:
                known_update(known, symbol_value)
            flag = True
        except Exception:
            pass

        for ineq in item.ineqs:
            if isinstance(ineq, base.BaseEq):
                known["equations"].append(ineq.value)
                if flag: 
                    known["processed"].append(ineq.value)
            elif isinstance(ineq, base.BaseIneq):
                known["inequations"].append(ineq.value)
                if flag: 
                    known["processed"].append(ineq.value)
            elif isinstance(ineq, base.BaseIneqs):
                known["inequations"].extend([inq.value for inq in ineq.ineqs])
                if flag: 
                    known["processed"].extend([inq.value for inq in ineq.ineqs])

    elif isinstance(item, base.BaseIneq):
        if len(item.free_symbols()) == 1:
            try:
                ans = FangChenBuDengShi001().solver(item).output[0].value
                key, value = list(ans.items())[0]
                if isinstance(value, (Interval, Union)) and key.is_Symbol:
                    known.update(ans)
                known["processed"].append(item.value)
            except Exception:
                pass

        known["inequations"].append(item.value)

    elif isinstance(item, base.BaseMultiple):
        res = any([known_update(known, obj) for obj in item.objs])
        print(res)
    else:
        return False


def auto_update(known, exprs):
    symbol_value = dict([(k, list(v)[0]) for k, v in known.items() if not isinstance(k, str) and isinstance(v, (list, FiniteSet)) and len(v) == 1])
    steps = []
    if not symbol_value:
        return steps

    tuple_keys = [(key, v) for key, v in symbol_value.items() if isinstance(key, (tuple, list)) and len(key) > 1]
    for key, values in tuple_keys:
        for k, v in zip(key, values):
            if not isinstance(v, Set):
                symbol_value.update({k: v})

    symbol_value = dict([(key, item) for key, item in symbol_value.items() if not isinstance(key, (tuple, list))])
    for k, v in known.items():
        if isinstance(v, base.BaseInnerSet):
            if v.subs(symbol_value):
                steps.append(["因为%s" % ",".join(["%s=%s" % (latex(a), latex(b)) for a, b in symbol_value.items()]), v.printing()])
        elif isinstance(v, (base.BaseFunc, base.BaseSinFunc)):
            # 将计算出的函数变量值代入函数方程中, 如: 设当x=θ时,函数f(x)=sinx-2cosx取得最大值,则cosθ=().
            if v.subs(symbol_value):
                steps.append(["", v.printing()])

        elif isinstance(v, Set):
            known[k] = v.subs(symbol_value)

        elif isinstance(v, (base.BasePoint, base.BaseZhiXian)):
            known[k] = v.subs(symbol_value)

        elif hasattr(v, "subs"):
            known[k] = v.subs(symbol_value)

    for k, v in exprs.items():
        if hasattr(v, "subs"):
            v.subs(symbol_value)

    known["equations"] = [[sympify(eq[0]).subs(symbol_value), sympify(eq[1]).subs(symbol_value)] for eq in known["equations"]]
    known["equations"] = [eq for eq in known["equations"] if eq[0] != eq[1]]
    known["inequations"] = [base.BaseIneq(ineq).subs(symbol_value) for ineq in known["inequations"] if len(ineq) == 3]
    known["inequations"] = [ineq.value for ineq in known["inequations"] if ineq.free_symbols()]
    return steps


class Question(object):
    def __init__(self, question):
        self.main = question["query"]
        self.text = question["text"]
        self.action = question["actions"] if "actions" in question else ""

    def __hash__(self):
        return hash(self.__repr__())

    def to_query(self):
        query = re.sub("[0-9]+", "", self.main)
        if "andequal" in query:
            query = query.replace("andequal", "")
            if query.isalpha():
                query = "OF(%s,值)" % query

        if query == "poly" or query == "variable" or query == "variables":
            query = "OF(%s,值)" % query

        return query.replace(" ", "")

    @staticmethod
    def name2value(name, value):
        if isinstance(value, base.BaseValue):
            return value
        else:
            obj = getattr(functions, "base" + name[0].upper() + name[1:])
        try:
            return obj(value)
        except Exception:
            return obj(*value)

    def get_input(self, name, exprs):
        if name == "text":
            return self.text

        if name == "action":
            return self.action

        if name == "main":
            return self.to_query()

        candidates = re.findall("[A-Za-z0-9_]+", self.main.split("=")[0])
        candidates += [cand for cand in re.findall("[A-Za-z0-9_]+", self.text) if cand not in candidates]
        candidates = [(cand, exprs[cand]) for cand in candidates if cand in exprs]

        inverse_num = 0
        if name[-1].isdigit():
            inverse_num = int(name[-1]) - 1
            name = name[:-1]

        for cand, expr in candidates:
            cand_name = re.sub("[0-9]+", "", cand)
            if name == cand_name or (not isinstance(getobj(name), str) and not isinstance(getobj(cand_name), str) and issubclass(getobj(cand_name), getobj(name))):
                if inverse_num == 0:
                    if name == "point" and cand_name == "yuan.txt":
                        return expr.to_point()

                    return expr
                else:
                    inverse_num -= 1

            if name == "func" and cand_name in ["eq", "curve"]:
                try:
                    if inverse_num == 0:
                        return self.name2value(cand_name, expr).toFunc()
                    else:
                        self.name2value(cand_name, expr).toFunc()
                        inverse_num -= 1
                except Exception:
                    pass
            elif name == "Inter" and cand_name == "Point":
                try:
                    if inverse_num == 0:
                        return self.name2value(cand_name, expr).to_inter()
                    else:
                        inverse_num -= 1
                except Exception:
                    pass

        return None


class Condition(Question):
    is_or = False

    def to_query(self):
        if "=" in self.main:
            query = self.main.split("=")[0]
        else:
            query = self.main
        return re.sub("[0-9]+", "", query).replace(" ", "")


class Conditions(object):
    is_or = False

    def __init__(self, cond1, cond2):
        self.conditions = {cond1, cond2}
        self.conditions2 = [cond1, cond2]
        self.main = ""
        self.text = cond1.text + "," + cond2.text

    def to_query(self):
        condition = list(self.conditions)
        return ["&&".join([condition[0].to_query(), condition[1].to_query()]), "&&".join([condition[1].to_query(), condition[0].to_query()])]

    def get_input(self, name, exprs):
        condition = Condition({"query": ",".join([cond.main for cond in self.conditions2 if hasattr(cond, "text")]), "text": ",".join([cond.text for cond in self.conditions2 if hasattr(cond, "text")])})
        if name == "text":
            return ",".join([cond.text for cond in self.conditions2 if hasattr(cond, "text")])
        return condition.get_input(name, exprs)


class Rule(object):
    def __init__(self, rule):
        self.id = rule["id"]
        self.rule_steps = []
        self.func = []
        for raw_step in rule["steps"]:
            try:
                func = getattr(functions, raw_step["func"])
            except Exception:
                func = raw_step["func"]
                raise ValueError("functions %s not in local package" % func)
            self.rule_steps.append({"input": raw_step["input"],
                                    "output": raw_step["output"],
                                    "func": func})
            self.func.append(raw_step["func"])

        self.steps = []
        self.labels = []
        self.last = None
        self.question = rule["question"]
        self.condition = rule["condition"]
        self.output = []
        self.raw_question = ""
        self.raw_condition = ""
        self.name = rule["name"]

    def printing(self):
        query = "(%s, %s)" % (self.question[0], self.condition[0])
        output = ",".join([str(out.value) for out in self.last]) if self.last else ""
        return "Query: %s Output:%s" % (query, output)

    @staticmethod
    def clean(string):
        new_string = string
        for a, b in [("NN", "N")]:
            if a in string:
                new_string = new_string.replace(a, b)
        return new_string

    def run(self, question, condition, problem, remote=""):
        exprs = problem.exprs
        known = problem.known
        target = problem.target

        local_dict = {}
        self.steps = []
        self.labels = []
        self.output = []

        verbose = False if condition else True
        for i, step in enumerate(self.rule_steps):
            _input = []
            for inp in step["input"]:
                if "(" in inp:
                    # 针对规则中加入的自定义变量, eg: ShuLieGetSums(question_Sequence, BaseVariable("n"))
                    if inp.find("BaseSequence") >= 0:
                        value = eval(inp)
                    else:
                        value = eval("base." + inp)

                elif '"' in inp:
                    value = inp.strip('"')

                elif inp == "exprs":
                    value = exprs

                elif inp in local_dict:
                    value = local_dict[inp]

                elif "question_" in inp:
                    inp_name = inp.replace("question_", "")
                    value = question.get_input(inp_name, exprs)

                elif "condition_" in inp:
                    inp_name = inp.replace("condition_", "")
                    value = condition.get_input(inp_name, exprs)
                else:
                    raise ValueError("input %s not found" % inp)

                # assert value is not None, "Fail to get variable: %s" % inp
                _input.append(value)

            if len(remote) == 0:
                func = step["func"](known=known, verbose=verbose, target=target, exist_symbols=problem.exist_symbols).solver(*_input)
                if func.printing():
                    self.steps.append({"labels": ",".join(func.label), "steps": [[self.clean(a), self.clean(b)] for a, b in func.printing()]})
                self.labels.extend(func.label)
                output = func.output
            else:
                output = self.remote(step["func"], _input, known, target, remote)

            if i < len(self.rule_steps) - 1:
                for item in output:
                    known_update(known, item)
                # auto_update(known, exprs)

            if step["output"]:
                try:
                    local_dict[step["output"]] = output[0]
                except Exception:
                    pass

            if output:
                self.output.append(output[0])

        self.last = output
        if question:
            self.raw_question = question.text

        if condition:
            self.raw_condition = condition.text
        return


class Problem(object):
    def __init__(self, parse_result, exprs, conds=None, exist_symbols=None):
        if conds is None:
            conds = []
        self.questions = [Question(question) for question in parse_result["questions"]]
        self.conditions = [Condition(condition) if isinstance(condition, dict) else condition for condition in parse_result["conditions"]]
        self.exprs = exprs
        self.solution = Solution([], [], [])
        self.fail_pairs = []
        self.known = {"equations": [], "inequations": [], "optimize": [], "func_equations": [], "func_assign": {}, "func_piecewise": [], "processed": []}
        if conds:
            self.known["inequations"].extend(conds)

        self.exist_symbols = exist_symbols
        self.target = []
        self.geosolver = None
        self.deduced = False

        for q in self.questions:
            if "Variables" in q.to_query():
                self.target.append(q.get_input("Variables", self.exprs))
            elif "Variable" in q.to_query():
                self.target.append(q.get_input("Variable", self.exprs))
            elif "target" in q.to_query():
                self.target.append(q.get_input("Variable", self.exprs))

        for name, obj in self.exprs.items():
            if isinstance(obj, (base.BasePoint, base.BaseFunc, base.BaseSinFunc)) and obj.name != "y":
                known_update(self.known, obj)
            elif isinstance(obj, base.BaseMultiple):
                for o in obj.objs:
                    if isinstance(o, (base.BasePoint, base.BaseGraph, base.BaseFunc, base.BaseSinFunc)) and o.name != "y":
                        known_update(self.known, o)

    def deduce(self):
        if self.deduced:
            return
        if "Vequations" in self.known and self.target:
            from mathsolver.functions.xiangliang.Vector_GetVariable import XLGetVariable
            stepsolver = XLGetVariable(known=self.known).solver(self.target[0])
            self.solution.steps.append({"labels": "", "steps": stepsolver.steps})
            ans = stepsolver.output[0].value
        elif "Eequations" in self.known and self.target:
            from mathsolver.functions.erxiangshi.EXS_JieFangCheng import EXSSolveEq
            stepsolver = EXSSolveEq(known=self.known).solver(self.target[0])
            self.solution.steps.append({"labels": "", "steps": stepsolver.steps})
            ans = stepsolver.output[0].value
        elif "Eequations" in self.known and not self.target:
            from mathsolver.functions.erxiangshi.EXS_JieFangCheng import EXSSolveEq
            stepsolver = EXSSolveEq(known=self.known).solver(base.BaseVariable('n'))
            self.solution.steps.append({"labels": "", "steps": stepsolver.steps})
            ans = stepsolver.output[0].value
        elif "Sequations" in self.known and self.target:
            from mathsolver.functions.shulie.ShuLie_getVariable import ShuLieGetVariable
            stepsolver = ShuLieGetVariable(known=self.known).solver(self.target)
            self.solution.steps.append({"labels": ",".join(stepsolver.label), "steps": stepsolver.steps})
            ans = stepsolver.output[0].value
        else:
            equations = self.known["equations"]
            unequations = list(self.known["inequations"])
            hypos = [v for v in self.known.values() if isinstance(v, base.BaseHypo)]
            if not equations and not unequations and not hypos:
                return
            if len(equations) == 0 and len(unequations) >= 1 and self.target and self.target[0].value != "x":
                unequations = [item for item in unequations if list(base.BaseIneq(item).free_symbols()) != [self.target[0].sympify()]]
                unequations = sorted(unequations, key=lambda x: len(base.BaseIneq(x).free_symbols()), reverse=True)
                try:
                    if Symbol("x") in self.known:
                        x_value = self.known[Symbol("x")]
                        stepsolver = HengChengLiRenYiXing(known=self.known).solver(base.BaseIneq(unequations[0]), base.BaseBelong(['x', "\\in", x_value]))
                    else:
                        stepsolver = HengChengLiRenYiXing(known=self.known).solver(base.BaseIneq(unequations[0]))
                    ans = stepsolver.output[0].value
                    self.solution.steps.append({"labels": ",".join(stepsolver.label), "steps": stepsolver.steps})
                except Exception:
                    return
            elif hypos:
                hypo = hypos[0].value
                if re.search("[fghFGH]\\(.*\\)", "".join(hypo["hypo"])):
                    obj = MtHypo()
                    v = obj.classify(hypo["hypo"])
                    condition = obj.classify(hypo["condition"])
                    v = ProcessExpression(known=self.known).solver(BaseValue(v.value)).output[0]
                    if hypo["flag"] == "exist":
                        stepsolver = HengChengLiCunZaiXing(known=self.known).solver(v, condition)
                    elif hypo["flag"] == "forall":
                        stepsolver = HengChengLiRenYiXing(known=self.known).solver(v, condition)

                    ans = stepsolver.output[0].value
                    self.solution.steps.append({"labels": ",".join(stepsolver.label), "steps": stepsolver.steps})
            elif not [item for item in unequations + equations if item not in self.known["processed"]]:
                return
            else:
                symbol_value = [(k, v) for k, v in self.known.items() if
                                not isinstance(k, str) and isinstance(v, (Interval, Union))]
                for k, v in symbol_value:
                    if not isinstance(v, Union):  # 并集转换有问题
                        unequations.extend(interval_2_ineqs(v, k))

                ineqs = base.BaseIneqs(unequations + equations)
                symbols = ineqs.free_symbols()
                target_symbol = [str(sym) for sym in symbols if "x" in str(sym) or "y" in str(sym)]
                if not target_symbol:
                    target_symbol = [str(sym) for sym in symbols]

                try:
                    ans = MathematicaSolve().solver(base.BaseIneqs(unequations + equations), base.BaseVariables(target_symbol)).output[0].value
                    assert "Root" not in str(ans)
                except Exception:
                    ans = MathematicaSolve().solver(ineqs).output[0].value
                self.solution.steps.append({"labels": "", "steps": [["解方程%s" % ineqs.printing(), print_ans(ans)]]})

        if self.known["optimize"]:
            opt = self.known["optimize"][0]
            symbols = opt.free_symbols()
            expr = opt.sympify()
            unknown_symbols = set(symbols).difference([Symbol("x"), Symbol("y")])
            ans = search_multiple(ans, unknown_symbols)

            if isinstance(ans, PieceWise):
                for values, cond in ans.pieces:
                    symbol_value = [(sym, v) for sym, v in zip(unknown_symbols, values)]
                    new_expr = expr.subs(symbol_value)
                    opt.value = new_expr
                    temp_symbols = new_expr.free_symbols
                    if not temp_symbols:
                        continue
                    temp_cond = [cond[sym] for sym in temp_symbols if sym in cond]
                    if temp_cond:
                        opt.cond = temp_cond[0]
                    new_ans = MathematicaOptimize().solver(opt, None)
                    value, new_symbol_value, _ = new_ans.output

                    eqs = [base.BaseEq([k, list(v)[0]]) for k, v in new_symbol_value.value.items()]
                    eqs += [base.BaseEq([sym, v]) for sym, v in zip(unknown_symbols, values)]
                    self.solution.steps.append({"labels": "最值问题", "steps": [["当%s时" % ",".join([eq.printing() for eq in eqs]), "%s取得最值" % latex(expr)]]})
                    ineqs.ineqs.extend(eqs)
                    ans = MathematicaSolve().solver(ineqs, base.BaseVariables(target_symbol)).output[0].value
                    self.known["optimize"] = []
                    break

        self.known.update(ans)
        key, value = list(ans.items())[0]
        if isinstance(value, FiniteSet) and len(value) == 1:
            auto_update(self.known, self.exprs)
        self.deduced = True
        return

    def auto_replace(self):
        """
        自动更新一些特殊类型，如函数f，数列a_{n}, 如 >已知函数f(x)=xlnx.求f(x)的最小值< 会将f(x)替换成f(x)=xlnx
        :return:
        """
        replace_dict = {}
        new_exprs = {}
        for k, v in self.exprs.items():
            if isinstance(v, base.BaseFuncName) and str(v.var) != "x":
                continue

            name = None
            n = re.sub("[0-9]+", "", k)
            num = re.findall("[0-9]+", k)[0]

            if hasattr(v, "name"):
                name = v.name
            elif "variable" == n:
                name = v.value

            try:
                sympy_name = sympify(name)
            except Exception:
                sympy_name = ""

            if isinstance(name, list):
                continue

            if name in self.known and not isinstance(self.known[name], base.BaseMingTiInner) and not isinstance(v, base.BaseFuncNameD):
                if not name:
                    if isinstance(v, type(self.known[name])) or isinstance(v, base.BasePoint):
                        continue
                if v.value == self.known[name].value:
                    continue
                obj = self.known[name]
                new_exprs[k] = obj
                if isinstance(obj, BaseValue):
                    obj_name = obj.__class__.__name__.replace("Base", "")

                    if "andequal" in k:
                        obj_name += "andequal"

                    obj_name += num
                    replace_dict[k] = obj_name
                    new_exprs[obj_name] = obj
            elif sympy_name and sympy_name in self.known:
                obj = self.known[sympy_name]
                if isinstance(obj, FiniteSet) and len(obj) == 1 and "I" in str(obj):
                    obj = base.BaseComplexPoly(list(obj)[0])
                    obj_name = "complexpoly"
                    obj_name += num
                    replace_dict[k] = obj_name
                    new_exprs[obj_name] = obj

            elif isinstance(v, base.BaseMultiple):
                v.objs = [self.known[obj.name] if hasattr(obj, "name") and obj.name and obj.name in self.known else obj
                          for obj in v.objs]

        self.exprs.update(new_exprs)
        if replace_dict:
            for token in self.questions + self.conditions:
                for k, v in replace_dict.items():
                    token.main = token.main.replace(k, v)

    def get_pairs(self):
        """
        Problem的conditions和questions进行排列组合, 形成condition和question对.
        :return:
        """
        pairs = []
        if len(self.conditions) == 1:
            pairs += [(question, condition) for question, condition in itertools.product(self.questions, self.conditions) if ";" not in condition.main]
            pairs.append(("", self.conditions[0]))
        elif len(self.conditions) > 1:
            pairs.append(("", self.conditions[0]))
            pairs.append(("", self.conditions[1]))
            pairs += [(question, condition) for question, condition in itertools.product(self.questions, self.conditions[-1:]) if ";" not in condition.main]

        if len(self.conditions) == 0:
            pairs += [(question, "") for question in self.questions]

        if len(self.conditions) >= 2:
            multicondition = [Conditions(self.conditions[0], self.conditions[1])]
            pairs += [(question, condition) for question, condition in itertools.product(self.questions, multicondition) if ";" not in condition.main]
            pairs += [("", condition) for condition in multicondition]
        try:
            return [tup for tup in pairs if tup not in self.fail_pairs]
        except Exception as E:
            print(E)

    def alter(self, question, condition, rule, obj=""):
        questions = [quest for quest in self.questions if quest != question]

        if isinstance(condition, Conditions):
            conditions = [cond for cond in self.conditions if cond not in condition.conditions]
        else:
            conditions = [cond for cond in self.conditions if cond != condition]
        assert (not question and not condition) or len(questions) < len(self.questions) or len(conditions) < len(
            self.conditions)

        new_problem = copy.deepcopy(self)
        inner_steps = None
        if condition and question == "":
            # 将condition得到的结果更新到known里面
            new_problem.conditions = conditions
            if obj:
                new_problem.known[obj] = rule.last[0]
            else:
                for item in rule.last:
                    if known_update(new_problem.known, item, condition.is_or) is False:
                        new_problem.conditions.append(item)

            inner_steps = auto_update(new_problem.known, new_problem.exprs)
            new_problem.auto_replace()

        elif question and condition == "":
            new_problem.questions = questions  # + [out for out in rule.last if out.intermediate == True]
            # for item in rule.last:
            #     known_update(new_problem.known, item)
            #     new_problem.questions.append(item)
        else:
            new_problem.questions = questions + [out for out in rule.last if out.intermediate]
            new_problem.conditions = conditions  # + rule.last

        if rule.steps:
            new_problem.solution.steps.extend(rule.steps)  # append({"labels":"".join(rule.labels), "steps":rule.steps})

        if rule.last and isinstance(rule.last[0], GeoSolver):
            new_problem.solution.steps.extend(rule.last[0].steps)

        if inner_steps:
            new_problem.solution.steps.append({"labels": "", "steps": inner_steps})

        eqs = [cond for cond in new_problem.conditions if isinstance(cond, base.BaseEq) and not cond.is_or]
        if len(eqs) > 1:
            comb = base.BaseEqs([eq.value for eq in eqs])
            new_problem.conditions = [comb] + [cond for cond in new_problem.conditions if
                                               not isinstance(cond, base.BaseEq)]

        new_problem.solution.labels += list(rule.labels)
        new_problem.solution.output += rule.output
        new_problem.solution.funcs += rule.func
        new_problem.solution.log.append(
            {"type": rule.name, "func": rule.func, "label": rule.labels, "question": rule.raw_question,
             "condition": rule.raw_condition})
        return new_problem


class Solution(object):
    def __init__(self, steps=None, labels=None, output=None):
        if output is None:
            output = []
        if labels is None:
            labels = []
        if steps is None:
            steps = []
        self.trace = []
        self.steps = steps
        self.labels = labels
        self.output = output
        self.funcs = []
        self.answer = {}
        self.log = []
        self.tokens = []

    def __add__(self, solution):
        self.steps += solution.steps
        self.labels += solution.labels
        self.output = solution.output
        self.funcs += solution.funcs
        self.tokens += solution.tokens
        self.log += solution.log
        return self

    def add(self, solution, num):
        if num == 2:
            if self.output:
                self.answer = {1: self.output[-1].value}
            else:
                self.answer = {1: ""}
        if solution.output:
            self.answer.update({num: solution.output[-1].value})
        else:
            self.answer.update({num: ""})

        self.__add__(solution)
        return self


if __name__ == '__main__':
    print(base.BaseIneqs(["a", ">", "b", ">", "0"]).subs({}).value)
    # from sympy import *
    # known = {}
    # symbol_value_update(known,{Symbol("k"):Interval(1,3)},False)
    #
    # symbol_value_update(known,{(Symbol("k"),Symbol("y")): FiniteSet(*[(Interval(1,2),2),(Interval(1,2),3)])},False)
